package com.magenta.gis.lira;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;

import javax.annotation.Resource;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.jolbox.bonecp.BoneCPDataSource;
import com.magenta.reflection.util.Reflector;

public class ResourceInjector {
	
	private static final Logger LOG = LoggerFactory.getLogger(ResourceInjector.class);
	
	private static InitialContext lazyInitializedCtx;
	
	private static Collection<String> deployedResources = new HashSet<String>();

	private ResourceInjector() { }
	
	public static void initialize( Object instance ) throws NamingException {
		//FOUND @Resource ANNOTATED FIELDS AND INJECT JNDI RESOURCES
		Class<?> type = instance.getClass();
		for ( Field declaredField : type.getDeclaredFields() ) {
			Resource resourceAnnotation;
			if ( (resourceAnnotation = declaredField.getAnnotation(Resource.class)) != null ) {
				Reflector.setValue(instance, declaredField.getName(), getContext().lookup(resourceAnnotation.mappedName()));
			}
		}
	}
	
	public static void deployJBossDataSource(String resourceName) throws IOException, NamingException, ClassNotFoundException {
		if ( deployedResources.contains(resourceName) ) {//DEPLOY ONCE ONLY
			return;
		}
		ClassLoader cl = Thread.currentThread().getContextClassLoader();
		Enumeration<URL> resources = cl.getResources(resourceName);
		if ( !resources.hasMoreElements() ) {
			LOG.warn("RESOURCES [%s] NOT FOUND!", resourceName);
		}
		for ( ;resources.hasMoreElements(); ) {
			URL resourceUrl = resources.nextElement();
			InitialContext ctx = getContext();
			DataSourceDescriptor dsDescriptor = parseJBossDataSource(resourceUrl);
			DataSource ds = getDataSource(dsDescriptor);
			if ( alreadyBinded(ctx, dsDescriptor.getJndiName()) ) {
				ctx.rebind(dsDescriptor.getJndiName(), ds);
			} else {
				ctx.bind(dsDescriptor.getJndiName(), ds);
			}
			String jndiName;
			if ( alreadyBinded(ctx, jndiName = "java:/" + dsDescriptor.getJndiName())) {
				ctx.rebind(jndiName, ds);//ALIAS
			} else {
				ctx.bind(jndiName, ds);//ALIAS
			}
		}
		deployedResources.add(resourceName);
	}
	
	private static boolean alreadyBinded(InitialContext ctx, String jndiName) {
		try {
			return ctx.lookup(jndiName) != null;
		} catch (NamingException e) {
			return false;
		}
	}

	private static DataSource getDataSource(DataSourceDescriptor dsDescriptor) throws ClassNotFoundException {
		Class.forName(dsDescriptor.getDriverClass());
		BoneCPDataSource ds = new BoneCPDataSource();
		ds.setJdbcUrl(dsDescriptor.getConnectionUrl());
		ds.setUsername(dsDescriptor.getUserName());
		ds.setPassword(dsDescriptor.getPassword());
		return ds;
	}

	private static DataSourceDescriptor parseJBossDataSource(URL resourceUrl) throws IOException {
		InputStream in = resourceUrl.openStream();
		DataSourceDescriptor dsDescriptor = (DataSourceDescriptor) XmlMapper.firstPattern(
				"select new com.magenta.gis.lira.DataSourceDescriptor(jndi-name, connection-url, driver-class, user-name, password, config-property) " +
				"from /datasources/local-tx-datasource", in);
		in.close();
		return dsDescriptor;
	}

	private static InitialContext getContext() throws NamingException {
		if ( lazyInitializedCtx == null ) {
			synchronized ( ResourceInjector.class ) {
				if ( lazyInitializedCtx == null ) {
					lazyInitializedCtx = new InitialContext();
				}
			}
		}
		return lazyInitializedCtx;
	}
	
}
